package cs342.project1.mathNotebook.data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JOptionPane;

import cs342.project1.mathNotebook.expressions.*;

//Comment Added
/**
 * Allows strings to be parse and then put into postorder in oder to create an evaluation tree
 * 
 * @author Alejandro Carrasquilla
 */
public class Parser {
	
	private EvaluationVisitor visitor;
	ExprFactory factory;
	Stack<String> postFix = new Stack<String>();
	
	/**
	 * Main method used for testing
	 * 
	 * @param args
	 */
	public static void main(String args[]){
		
		String inString = JOptionPane.showInputDialog("Enter your Expr: ");	
		new Parser(inString);
		System.out.println(inString);	
		
	}
	
	public Parser(){
		
	}
	/**
	 * Constructor with one argument. Used for testing.
	 * @param test
	 */
	public Parser(String test){
		postFix = new Stack<String>();
		try{
		parseExpression(postFix,test);
		
		createExpTree(postFix);
		}catch(Exception e){
			System.out.println("Something is wrong");
		}
	}
	/**
	 * Method used to get a expression from a string
	 * 
	 * @param inString - input string
	 * @return Expression
	 */
	public Expr getExpression(String inString) throws Exception{
		postFix = new Stack<String>();
		parseExpression(postFix,inString);
		
		return createExpTree(postFix);
	}
	/**
	 * Create post fix and put in post fix stack
	 * 
	 * @param postFix
	 * @param inString
	 */
	public void parseExpression(Stack<String> postFix,String inString) throws Exception{
		//try{
		
		Pattern p = Pattern.compile("\\d+(\\.\\d+)?|\\+|\\*|\\-|\\/|c+|s+|t+|\\^|\\(|\\)|\\~|x+|[^xcts]");

		Matcher matcher = p.matcher(inString);
		
		//Operators methods
		factory = new ExprFactory();
		
		Stack<Operator> operators = new Stack<Operator>();
		
		while(matcher.find()){
			
			String currentS = matcher.group();
			if(currentS.length() == 0) break;
			
			char ch = currentS.charAt(0);	
			
			if(!Character.isDigit(ch) && ch != 'x'){
				Operator current;
				if(currentS.equals("(")){//Left parenthesis
					current = new Operator(Character.toString(ch),0);
					operators.push(current); // push it	
				}else if(currentS.equals(")")){//Right parenthesis
					while (!operators.isEmpty()) {		
						String last = operators.pop().operator;	
						if (last.equals("(")){ 			
							break; 		
						}else{	
							postFix.push(last);
						}
					}
				}else{//Any other Symbol
					if(matcher.hitEnd())
						throw new Exception();
					current = new Operator(Character.toString(ch),factory.precedence(Character.toString(ch)));
					if(!operators.isEmpty()){
						if(operators.peek().presedence >= current.presedence){
							String temp = operators.pop().operator;
							postFix.push(temp);
						}
					}
					operators.push(current);					
				}
			}else{//Operand
				postFix.push(currentS);
			}	
		} 
		while (!operators.isEmpty()) {	
			String operator = operators.pop().operator;
			postFix.push(operator);
		}	
		
		//printStack(postFix);
		
		//}catch(Exception e){
		//	System.out.println("Something went wrong parsing the expression " + e);
		//}
	}
	/**
	 * Prints contents of a stack. Used for testing
	 * 
	 * @param stack
	 */
	static void printStack(Stack<String> stack){
		while(!stack.isEmpty()){
			System.out.println(stack.pop());
		}
	}
	/**
	 * Creates the tree expression based on the post fix stack
	 * 
	 * @param out
	 * @return
	 */
	public Expr createExpTree(Stack<String> out) throws Exception{
		
		visitor = new EvaluationVisitor();
		Stack<Expr> tree = new Stack<Expr>();
		Expr temp = null;

		//try{
	
		Expr a2 = null;
		Expr b2 = null;
		
		LiteralExpr currentL = null;
		VarExpr currentV = null;
		
		ArrayList<String> postfixList = new ArrayList<String>();

		while(!out.isEmpty()){
			postfixList.add(out.pop());
		}
		
		for(int j = postfixList.size()-1; j >= 0; j--){
			
			String current = postfixList.get(j);
			char ch = current.charAt(0);
			
			if(!Character.isDigit(ch) && ch!='x'){
				
				switch(factory.operands(Character.toString(ch))){
					case 1:
						a2 = tree.pop();
						Expr[] one = new Expr[1];
						one[0] = a2;
						tree.push(factory.createExpression(Character.toString(ch),one));
						break;
					case 2:
						a2 = tree.pop();
						b2 = tree.pop();
						Expr[] two = new Expr[2];
						two[0] = b2;
						two[1] = a2;
						tree.push(factory.createExpression(Character.toString(ch),two));
						break;
					default:
						System.out.println("Something went wrong");
						throw new Exception();
						//System.out.println("Something went wrong");
						//break;
				}	
			}else{
				if(ch == 'x'){
					if(current.equals("x")){
						currentV = new VarExpr(postfixList.get(j));
						tree.push(currentV);
					}else{
						throw new Exception();
					}
				}else {
					currentL = new LiteralExpr(Double.parseDouble(postfixList.get(j)));
					tree.push(currentL);
				}
			}	
		}
		
		temp = tree.pop();
		if(tree.size()!=0){
			//System.out.println("Something went wrong creating the tree");
			throw new Exception();
		}
		//}catch(Exception e){
			//System.out.println("Something went wrong creating the tree");
		//	new Exception();
		//}
		return temp;
	}
	
}


/**
 * Represents a Operator
 * 
 * @author Alejandro Carrasquilla
 *
 */
class Operator{
	String operator;
	int presedence;
	
	public Operator(String operator,int presedence){
		 this.operator = operator;
		 this.presedence = presedence;
	}
}